// Copyright (C) 2010  Joan Puigcerver Perez, <joapuipe@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include <Movies.hpp>

#include <iostream>
#include <fstream>
#include <cstring>

#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/util/XMLString.hpp>

#include <SAXMoviesHandler.hpp>

Movies::Movies(std::ostream& logf, ost::Mutex& logm)
  : logfile(logf), log_mutex(logm)
{
}

Movies::~Movies()
{
  for(std::vector<Movie*>::iterator it = movies.begin(); it != movies.end(); ++it)
    delete *it;
}

bool Movies::load(Database * database)
{
  try {
    XMLPlatformUtils::Initialize();
  } 
  catch( const XMLException & toCatch ) {
    char* message = XMLString::transcode(toCatch.getMessage());
    print_log_line("Error during initialization! :", logfile, log_mutex);
    print_log_line("Exception message is:", logfile, log_mutex);
    print_log_line(message, logfile, log_mutex);
    XMLString::release(&message);
    return false;
  }
  
  SAX2XMLReader* parser = XMLReaderFactory::createXMLReader();
  
  SAXMoviesHandler handler(database, logfile, log_mutex);
  parser->setContentHandler(&handler);
  parser->setErrorHandler(&handler);
  
  try {
    parser->parse(MOVIES_XML);
  }
  catch (const XMLException& toCatch) {
    char* message = XMLString::transcode(toCatch.getMessage());
    print_log_line("Exception message is:", logfile, log_mutex);
    print_log_line(message, logfile, log_mutex);
    XMLString::release(&message);
    delete parser;
    XMLPlatformUtils::Terminate();
    return false;
  }
  catch (const SAXParseException& toCatch) {
    char* message = XMLString::transcode(toCatch.getMessage());
    print_log_line("Exception message is:", logfile, log_mutex);
    print_log_line(message, logfile, log_mutex);
    XMLString::release(&message);
    delete parser;
    XMLPlatformUtils::Terminate();
    return false;
  }
  catch (...) {
    print_log_line("Unexpected exception!", logfile, log_mutex);
    delete parser;
    XMLPlatformUtils::Terminate();
    return false;
  }
  
  delete parser;
  XMLPlatformUtils::Terminate();
  return true; 
}

Movie* Movies::add_movie(const std::string& title, const Year year, 
			 const Media media, const bool suspended)
{
  // It tries to create a new node in movies_map. 
  // If the title didn't exist or it was not a main title, then a new Movie object is created
  // and stored. A pointer to the created movie is returned.
  // If the title existed and it was a main title, then no movie is inserted a null pointer is returned.
  std::pair<mapIterator,bool> ret = movies_map.insert ( std::pair<const std::string, MapNode>(title, MapNode()) );
  if ( ret.second == true || ret.first->second.main == 0 ) {
    Movie * res = new Movie(movies.size(), ret.first->first, year, media, suspended);
    movies.push_back( res );
    ret.first->second.main = res;
    return res;
  } else 
    return 0;
}

void Movies::add_aka(const std::string& aka, Movie* movie)
{
  std::pair<mapIterator,bool> ret = movies_map.insert ( std::pair<const std::string, MapNode>(aka, MapNode()) );
  ret.first->second.akas.push_back( movie );
}

Movie* Movies::exact_search(const std::string& title)
{
  // If the title does not match with any movie, then null pointer is returned.
  // If the title match with a main title of a movie, then this movie is returned.
  // If the title match with an aka title for a single movie, then this movie is returned.
  // If the title match with an aka title for more than one movie, then null pointer is returned.
  mapIterator it = movies_map.find( title );
  if ( it == movies_map.end() ) return 0;
  if ( it->second.main != 0) return (Movie*)it->second.main;
  if ( it->second.akas.size() == 1 ) return (Movie*)it->second.akas.front();
  return 0;
}

size_t Movies::size(void) const
{
  return movies.size();
}
